﻿// ----------------------------------------------------------------------------------
// Sample Code from Fernando Machado Píriz's blog: http://fernandomachadopiriz.com
//
// <copyright company="Fernando Machado Píriz" file="MainViewModel.cs">
// Copyright (c) Fernando Machado Píriz. Distributed under FSF's GPL v2.</copyright>

// Copyright (c) Fernando Machado Píriz. Distributed under FSF's GPL v2.</copyright>
// ----------------------------------------------------------------------------------
// This code and information are provided "as is" without warranty of any kind,
// either expressed of implied, including but not limited to the implied warranties
// of merchantability and/or fitness for a particular purpose.
// ----------------------------------------------------------------------------------
namespace GivingAPresentation
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.Diagnostics.CodeAnalysis;
    using System.Windows.Input;
    using GivingAPresentation.Commons;

    /// <summary>
    /// The view model of the <code>MainView</code> view using the Model View View Model pattern.
    /// </summary>
    public class MainViewModel : BaseViewModel, IDisposable
    {
        /// <summary>
        /// The backend field for <code>Parts</code> property.
        /// </summary>
        [ImportMany(typeof(IContract))]
        private IList<Lazy<IContract, IMetadata>> parts = new List<Lazy<IContract, IMetadata>>();

        /// <summary>
        /// The backend field for the <code>Model</code> property.
        /// </summary>
        private MainModel model;

        /// <summary>
        /// A value indicating if extension parts were loaded or not.
        /// </summary>
        private bool extensionsLoaded;

        /// <summary>
        /// The container of composing parts.
        /// </summary>
        private CompositionContainer container;

        /// <summary>
        /// The catalog to load composing parts from.
        /// </summary>
        private DirectoryCatalog catalog;

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "All exceptions needs to be caught to be shown later")]
        public MainViewModel()
        {
            this.model = new MainModel();
            this.model.PropertyChanged += new PropertyChangedEventHandler(this.Model_PropertyChanged);

            this.InitializeParts = new RelayCommand(
                argument => this.DoInitializeParts(),
                result => !this.model.Presenting && !this.extensionsLoaded);

            this.FinalizeParts = new RelayCommand(
                argument => this.DoFinalizeParts(),
                result => !this.model.Presenting && this.extensionsLoaded);

            IList<Exception> exceptions = new List<Exception>();

            // Extensibility composition
            this.catalog = new DirectoryCatalog(".");
            this.container = new CompositionContainer(this.catalog);
            try
            {
                this.container.ComposeParts(this);
            }
            catch (Exception e)
            {
                exceptions.Add(e);
            }

            ExceptionHelper.ShowExceptions(exceptions);
        }

        /// <summary>
        /// Gets the list of component parts. Each part is used to change certain settings during a presentation. The list will be filled with
        /// instances created and loaded by the Managed Extensibility Framework.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
            Justification = "Generic types can be nested because instances will be managed by the Managed Extensibility Framework")]
        public IList<Lazy<IContract, IMetadata>> Parts
        {
            get
            {
                return this.parts;
            }
        }

        /// <summary>
        /// Gets the model of this view model in the Model View View Model pattern.
        /// </summary>
        public MainModel Model
        {
            get
            {
                return this.model;
            }
        }

        /// <summary>
        /// Gets the command used to fire "initialization" of parts before they are used for the first time. The semantics of "initialize" is
        /// defined by parts but usually means loading previously stored state. This command has no arguments.
        /// </summary>
        public ICommand InitializeParts { get; private set; }

        /// <summary>
        /// Gets the command used to fire "finalization" of parts when they are not needed anymore. The semantics of "finalize" is defined by parts
        /// but usually means storing currently configured state. This command has no arguments.
        /// </summary>
        public ICommand FinalizeParts { get; private set; }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <param name="disposing">Indicates when this method is called by <code>Dispose</code>.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.container != null)
                {
                    this.container.Dispose();
                    this.container = null;
                }

                if (this.catalog != null)
                {
                    this.catalog.Dispose();
                    this.catalog = null;
                }
            }
        }

        #endregion // IDisposable

        /// <summary>
        /// Forwards the <code>StartPresentation</code> command to parts.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", 
            Justification = "All exceptions needs to be caught to be shown later")]
        private void DoStartPresentation()
        {
            IList<Exception> exceptions = new List<Exception>();

            foreach (Lazy<IContract, IMetadata> item in this.Parts)
            {
                try
                {
                    item.Value.StartPresentation.Execute(null);
                }
                catch (Exception exception)
                {
                    exceptions.Add(exception);
                }
            }

            ExceptionHelper.ShowExceptions(exceptions);
        }

        /// <summary>
        /// Forwards the <code>StopPresentation</code> command to parts.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "All exceptions needs to be caught to be shown later")]
        private void DoStopPresentation()
        {
            IList<Exception> exceptions = new List<Exception>();

            foreach (Lazy<IContract, IMetadata> item in this.Parts)
            {
                try
                {
                    item.Value.StopPresentation.Execute(null);
                }
                catch (Exception exception)
                {
                    exceptions.Add(exception);
                }
            }

            ExceptionHelper.ShowExceptions(exceptions);
        }

        /// <summary>
        /// Processes the <code>InitializePart</code> command by forwarding the command to parts.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "All exceptions needs to be caught to be shown later")]
        private void DoInitializeParts()
        {
            IList<Exception> exceptions = new List<Exception>();

            foreach (Lazy<IContract, IMetadata> item in this.Parts)
            {
                try
                {
                    item.Value.InitializePart.Execute(null);
                }
                catch (Exception exception)
                {
                    exceptions.Add(exception);
                }
            }

            ExceptionHelper.ShowExceptions(exceptions);
            this.extensionsLoaded = true;
        }

        /// <summary>
        /// Processes the <code>FinalizePart</code> command by forwarding the command to parts.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "All exceptions needs to be caught to be shown later")]
        private void DoFinalizeParts()
        {
            IList<Exception> exceptions = new List<Exception>();

            foreach (Lazy<IContract, IMetadata> item in this.Parts)
            {
                try
                {
                    item.Value.FinalizePart.Execute(null);
                }
                catch (Exception exception)
                {
                    exceptions.Add(exception);
                }
            }

            try
            {
                SettingsManager.Instance.Save();
            }
            catch (Exception exception)
            {
                exceptions.Add(exception);
            }

            ExceptionHelper.ShowExceptions(exceptions);
            this.extensionsLoaded = false;
        }

        /// <summary>
        /// Fired when a model's property changes. If model's Presenting property has changed starts or stops settings changes while the user
        /// is giving a presentation.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">A <code>System.ComponentModel.PropertyChangedEventArgs</code> that contains the event data.</param>
        private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender.Equals(this.model) && e.PropertyName.Equals("Presenting"))
            {
                if (this.model.Presenting)
                {
                    this.DoStartPresentation();
                }
                else
                {
                    this.DoStopPresentation();
                }
            }
        }
    }
}
